﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace TradeSea
{
    public abstract class Camera //describes a camera
    {
        protected Vector3 location;
        protected Matrix projection;
        public abstract Matrix getView(); //each camera must implement unique method for creating a view matrix
        public virtual Matrix getProjection()
        { return projection; }//can have a method for generating projection e.g speed -> wide fov, but fallback returns default projection
        public virtual Vector3 Location
        {
            get
            { return location; }
        }
    }
    public class TestCamera : Camera //Initial test camera translated into this class
    {
        Vector3 offsetFromTarget;
        Vector3 target;
        public TestCamera(Vector3 offsetFromTarget, Matrix projection, Vector3 target)
        {
            this.projection = projection;
            this.offsetFromTarget = offsetFromTarget;
        }

        public void Update(KeyboardState kstate, Vector3 target)
        {
            this.target = target;
            if (kstate.IsKeyDown(Keys.Down)) //keyboard input logic
                if (offsetFromTarget.Y > 0)
                    offsetFromTarget += new Vector3(0, -0.5f, 0);
            if (kstate.IsKeyDown(Keys.Up))
                if (offsetFromTarget.Y < 180)
                    offsetFromTarget += new Vector3(0, 0.5f, 0);
            if (kstate.IsKeyDown(Keys.Left))
                if (offsetFromTarget.X > -180 && offsetFromTarget.Z < 180)
                    offsetFromTarget += new Vector3(-0.5f, 0, 0.5f);
            if (kstate.IsKeyDown(Keys.Right))
                if (offsetFromTarget.X < 180 && offsetFromTarget.Z > -180)
                    offsetFromTarget += new Vector3(0.5f, 0, -0.5f);
            this.location = target + offsetFromTarget;
        }

        public override Matrix getView()
        {
            return Matrix.CreateLookAt(location, target, Vector3.Up);
        }
    }
    public class FPCamera : Camera //A first person camera
    {
        float pitch = 0f; //up down
        float swivel = 0f; //left right
        float headingOffset = 0;
        int centreX; //Centre of screen X
        int centreY; //Centre of screen Y
        float mouseSensitivity;

        public FPCamera(Vector3 location, Matrix projection, int centX, int centY, float mouseSensitivity)
        {
            this.location = location;
            this.projection = projection;
            centreX = centX; //The centrepoint of the window, needs to be changed when window size changed maybe?
            centreY = centY;
            this.mouseSensitivity = mouseSensitivity * 0.001f;
        }
        public void Update(Vector4 cameraLocAngle, Vector2 mousePos)
        {
            this.location = new Vector3(cameraLocAngle.X, cameraLocAngle.Y, cameraLocAngle.Z);
            headingOffset = cameraLocAngle.W;
            pitch += (mousePos.Y - centreY) * mouseSensitivity; //get diff in mouse x and y height from window centre, apply to pitch and swivel
            swivel += (mousePos.X - centreX) * mouseSensitivity;
            if (pitch > MathHelper.PiOver2)
                pitch = MathHelper.PiOver2;
            else if (pitch < -MathHelper.PiOver2)
                pitch = -MathHelper.PiOver2;
            swivel = swivel % MathHelper.TwoPi;
        }
        public override Matrix getView()
        {
            return Matrix.CreateTranslation(-location) * Matrix.CreateRotationY(swivel - headingOffset) * Matrix.CreateRotationX(pitch);
        }

    }
    public class MouseOrbitCamera : Camera
    {
        float pitch = 0f; //up down
        float swivel = 0f; //left right

        Vector3 target = new Vector3(0);

        int centreX; //Centre of screen X
        int centreY; //Centre of screen Y
        float pitchCeiling = MathHelper.PiOver2 - 0.01f;
        float pitchFloor = 0.05f;

        float dist;
        float mouseSensitivity;

        public MouseOrbitCamera(float dist, Matrix projection, int centX, int centY, float mouseSensitivity)
        {
            this.dist = dist;
            this.projection = projection;
            centreX = centX;
            centreY = centY;
            this.mouseSensitivity = mouseSensitivity * 0.001f;
        }

        public void Update(Vector3 target, Vector2 mousePos)
        {
            this.target = target;
            pitch += (mousePos.Y - centreY) * mouseSensitivity; //get diff in mouse x and y height from window centre, apply to pitch and swivel
            swivel += (mousePos.X - centreX) * mouseSensitivity;
            if (pitch > pitchCeiling)
                pitch = pitchCeiling;
            else if (pitch < pitchFloor)
                pitch = pitchFloor;
            swivel %= MathHelper.TwoPi;

            this.location = Vector3.Transform(Vector3.Backward * dist, Matrix.CreateRotationX(-pitch) * Matrix.CreateRotationY(swivel)) + target;
        }

        public override Matrix getView()
        {
            Vector3 pos = Vector3.Transform(Vector3.Backward * dist, Matrix.CreateRotationX(-pitch) * Matrix.CreateRotationY(swivel) ) + target;
            return Matrix.CreateLookAt(pos, target, Vector3.Up);
        }
    }

}
